unit CPAggPlotCanvas;

interface

uses Types, Classes, Controls, Windows, Graphics,
  CPPlotCanvas, CPBase, Agg2D;

type
  { This class implements painting methods by using Anti Grain GEometry as
    rendering library. Canvas units are interpreted as pixels. }
  TCPAggPlotCanvas = class(TCPPlotCanvas)
  private
    function ColorToRGB8(Color: TColor): TAggColor;
  protected
    procedure Resize; override;
    procedure SetBrushColor(const Value: TColor); override;
    procedure FontChanged; override;
    procedure SetPenColor(const Value: TColor); override;
    procedure SetPenWidth(const Value: TCPFloat); override;
  public
    FAgg2D: TAgg2D;
    FBitmap: TBitmap;
    { Create the canvas on the referenced parent control.
      @param Control Plotter control) }
    constructor Create(Control: TCustomControl;
      UnitsPerInch: TCPFloat); override;
    destructor Destroy; override;
    function SmoothDistance(Distance: TCPFloat): TCPFloat; override;
    procedure MoveTo(X, Y: TCPFloat); override;
    procedure LineTo(X, Y: TCPFloat); override;
    procedure HorizLine(Y: TCPFloat); override;
    procedure VertLine(X: TCPFloat); override;
    procedure Ellipse(const Bounds: TCPBounds); override;
    procedure Rectangle(const Bounds: TCPBounds); override;
    procedure FrameRect(const Bounds: TCPBounds); override;
    procedure FillRect(const Bounds: TCPBounds); override;
    procedure DrawLineSegments(Points: TCPFloatArray; Left,
      Right: TCPFloat; StepSize: Double); override;
    procedure TextOut(X, Y: TCPFloat; const Text: string); override;
    procedure BeginDraw; override;
    procedure EndDraw; override;
  end;

implementation

uses Math;

{ TCPAggPlotCanvas }

procedure TCPAggPlotCanvas.BeginDraw;
begin
  inherited;
  FAgg2D.ClearAll(ColorToRGB8(Color));

  //FBitmap.Canvas.Brush.Color := Color;
  //FBitmap.Canvas.FillRect(FCanvas.ClipRect);
end;

function TCPAggPlotCanvas.ColorToRGB8(Color: TColor): TAggColor;
begin
  Result.b := Color and $FF;
  Result.g := (Color shr 8) and $FF;
  Result.r := (Color shr 16) and $FF;
  Result.a := $FF;
end;

constructor TCPAggPlotCanvas.Create(Control: TCustomControl;
  UnitsPerInch: TCPFloat);
begin
  inherited;
  FControl.ControlStyle := FControl.ControlStyle + [csOpaque];
  FControl.DoubleBuffered := false; // Disable VLC doublebuffering
  SetFont(FCanvas.Font);
  FAgg2D := TAgg2D.Create;
  FBitmap := TBitmap.Create;
  FBitmap.PixelFormat := pf32bit;
  FAgg2D.LineCap(AGG_CapButt);
  FAgg2D.LineJoin(AGG_JoinMiter);
  FAgg2D.TextAlignment(AGG_AlignLeft, AGG_AlignTop);
  FAgg2D.TextHints(true);
  FAgg2D.Attach(FBitmap, False);
end;

destructor TCPAggPlotCanvas.Destroy;
begin
  FAgg2D.Free;
  FBitmap.Free;
  inherited;
end;

procedure TCPAggPlotCanvas.Ellipse(const Bounds: TCPBounds);
begin
  FAgg2D.Ellipse(Bounds.Left - Bounds.Width / 2,
    Bounds.Top - Bounds.Height / 2, Bounds.Width, Bounds.Height);
end;

procedure TCPAggPlotCanvas.EndDraw;
begin
  FCanvas.Draw(0, 0, FBitmap);
  inherited;
end;

procedure TCPAggPlotCanvas.FillRect(const Bounds: TCPBounds);
begin
  FAgg2D.NoLine;
  FAgg2D.Rectangle(Bounds.Left, Bounds.Top,
    Bounds.Left + Bounds.Width, Bounds.Top + Bounds.Height);
  FAgg2D.LineColor(ColorToRGB8(FPenColor));
end;

procedure TCPAggPlotCanvas.FrameRect(const Bounds: TCPBounds);
begin
  FAgg2D.Rectangle(Bounds.Left, Bounds.Top,
    Bounds.Left + Bounds.Width, Bounds.Top + Bounds.Height);
end;

procedure TCPAggPlotCanvas.HorizLine(Y: TCPFloat);
begin
  FAgg2D.ResetPath;
  FAgg2D.MoveTo(0, Y);
  FAgg2D.HorLineTo(FBounds.Width);
  FAgg2D.DrawPath(AGG_StrokeOnly);
end;

procedure TCPAggPlotCanvas.LineTo(X, Y: TCPFloat);
begin
  FAgg2D.Line(FDrawPos.X, FDrawPos.Y, X, Y);
  inherited;
end;

procedure TCPAggPlotCanvas.MoveTo(X, Y: TCPFloat);
begin
  inherited;
  FAgg2D.MoveTo(X, Y);
end;

procedure TCPAggPlotCanvas.Rectangle(const Bounds: TCPBounds);
begin
  FAgg2D.Rectangle(Bounds.Left, Bounds.Top,
    Bounds.Left + Bounds.Width, Bounds.Top + Bounds.Height);
end;

procedure TCPAggPlotCanvas.Resize;
begin
  inherited;
  FBitmap.Free;
  FBitmap := TBitmap.Create;
  FBitmap.PixelFormat := pf32bit;
  FBitmap.Height := FControl.Height;
  FBitmap.Width := FControl.Width;
  FBitmap.Canvas.Font := Font;
  FAgg2D.Attach(FBitmap);
end;

procedure TCPAggPlotCanvas.SetBrushColor(const Value: TColor);
begin
  inherited;
  FAgg2D.FillColor(ColorToRGB8(FBrushColor));
end;

procedure TCPAggPlotCanvas.FontChanged;
begin
  inherited;
  if (Assigned(FAgg2D)) then
    FAgg2D.Font(Font.Name, FFontHeight, fsBold in Font.Style,
      fsItalic in Font.Style);
end;

procedure TCPAggPlotCanvas.SetPenColor(const Value: TColor);
begin
  inherited;
  FAgg2D.LineColor(ColorToRGB8(FPenColor));
end;

procedure TCPAggPlotCanvas.SetPenWidth(const Value: TCPFloat);
begin
  inherited;
  FAgg2D.LineWidth(PtToUnits(FPenWidth));
end;

function TCPAggPlotCanvas.SmoothDistance(Distance: TCPFloat): TCPFloat;
begin
  Result := Distance;
end;

procedure TCPAggPlotCanvas.TextOut(X, Y: TCPFloat; const Text: string);
var
  OldFillColor: TAggColor;
begin
  OldFillColor := FAgg2D.FillColor;
  FAgg2D.FillColor(FAgg2D.LineColor);
  FAgg2D.NoLine;
  FAgg2D.Text(X, Y + FAgg2D.FontHeight, Text);
  FAgg2D.LineColor(FAgg2D.FillColor);
  FAgg2D.FillColor(OldFillColor);
  {SetBkMode(FBitmap.Canvas.Handle, TRANSPARENT);
  FBitmap.Canvas.Font.Color := FPenColor;
  Windows.TextOut(FBitmap.Canvas.Handle, Trunc(X), Trunc(Y),
    PChar(Text), Length(Text));}
end;

procedure TCPAggPlotCanvas.VertLine(X: TCPFloat);
begin
  FAgg2D.ResetPath;
  FAgg2D.MoveTo(X, 0);
  FAgg2D.VerLineTo(FBounds.Height);
  FAgg2D.DrawPath(AGG_StrokeOnly);
end;

procedure TCPAggPlotCanvas.DrawLineSegments(Points: TCPFloatArray; Left,
  Right: TCPFloat; StepSize: Double);
var
  Pt: TCPPoint;
  Index: Integer;
  DrawingLine: Boolean;
  YMin, YMax: TCPFloat;
begin
  Index := 0;
  DrawingLine := false;
  Pt.X := Left;
  FAgg2D.ResetPath;
  YMin := FBounds.Top - FBounds.Height;
  YMax := FBounds.Top + FBounds.Height * 2;
  while (Pt.X <= Right) do
  begin
    Pt.Y := EnsureRange(Points[Index], YMin, YMax);
    if (IsNan(Pt.Y) = false) and
       (IsInfinite(Pt.Y) = false) then
    begin
      if DrawingLine then
        FAgg2D.LineTo(Pt.X, Pt.Y)
      else
      begin
        FAgg2D.MoveTo(Pt.X, Pt.Y);
        DrawingLine := true;
      end;
    end
    else
      // Interrupt line, due to undefined value in definition range:
      DrawingLine := false;
    Inc(Index);
    // Move to next graph point:
    Pt.X := Pt.X + StepSize;
  end;
  FAgg2D.DrawPath(AGG_StrokeOnly);
  FAgg2D.ResetPath;
end;


end.
